#[macro_use]
extern crate log;

use std::thread;
use std::time::Duration;

use anyhow::{bail, Result};
use std::io::{Read, Write};
use std::net::{Shutdown, TcpListener, TcpStream};

fn main() {
    std::env::set_var("RUST_LOG", "trace");
    pretty_env_logger::init();

    info!("{:?} => program start", thread::current().id());
    match run() {
        Ok(_) => info!("Success"),
        Err(e) => error!("{}", e),
    }
}

fn run() -> Result<()> {
    let listener = TcpListener::bind("0.0.0.0:9000")?;

    info!("{:?} => listen on 0.0.0.0:9000", thread::current().id());
    while let Some(stream) = listener.incoming().next() {
        let stream = stream?;
        std::thread::spawn(|| handle(stream));
    }

    Ok(())
}

fn handle(mut stream: TcpStream) -> Result<()> {
    let req = read_tcp(&mut stream)?;
    info!("{:?} => receive req: {}", thread::current().id(), req);

    let mut rpc_client = TcpStream::connect("127.0.0.1:9100")?;
    let res = call_service(&mut rpc_client, req)?;
    rpc_client.shutdown(Shutdown::Both)?;

    let res = format!("{:010}{}", res.len(), res);
    stream.write_all(res.as_bytes())?;
    stream.flush()?;
    stream.shutdown(Shutdown::Both)?;
    info!("{:?} => send res: {}", thread::current().id(), res);
    Ok(())
}

fn call_service(rpc_stream: &mut TcpStream, req: impl ToString) -> Result<String> {
    let req = req.to_string();
    debug!("{:?} => rpc start", thread::current().id());

    //请求
    let req = format!("{:010}{}", req.len(), req);
    rpc_stream.write_all(req.as_bytes())?;
    rpc_stream.flush()?;
    debug!("{:?} => rpc done", thread::current().id());

    //响应
    let res = read_tcp(rpc_stream)?;
    Ok(res)
}

fn read_tcp(stream: &mut TcpStream) -> Result<String> {
    stream.set_read_timeout(Some(Duration::from_secs(30)))?;
    //读10位长度
    let mut len = [0_u8; 10];
    stream.read_exact(&mut len)?;
    let length: usize = String::from_utf8_lossy(&len).parse()?;
    if length < 1 {
        warn!(
            "{:?} => receive length less then 1: {}",
            thread::current().id(),
            length
        );
        bail!("length is 0")
    }

    //读指定长度的报文
    let mut msg = vec![0_u8; length];
    stream.read_exact(&mut msg)?;
    let msg = String::from_utf8(msg)?;

    Ok(msg)
}
